package net.yurushao.tool;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileNameExtensionFilter;

import net.yurushao.encrypt.Encrypter;
import net.yurushao.tool.strings.Command;
import net.yurushao.tool.strings.LogInfo;

public class EncryptionToolFrame extends JFrame {
	
	private static final int MODE_ONLINE = 1;
	private static final int MODE_OFFLINE = 2;
	
	private int mode = MODE_ONLINE;
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	JTextField mBookPath;			// 显示电子书所在路径
	JTextField mDeviceId;			// 输入设备号

	JTextArea mLogWindow;			// 日志窗口
	
	JLabel mInputHint;

	JButton mSelectBook;			// 打开filechooser选择电子书
	JButton mDetectDevice;			// 检测手机是否已经连接
	JButton mGetDeviceId;			// 获取设备号
	JButton mEncryptBook;			// 进行加密
	JButton mPushToDevice;			// 将加密后的电子书存到/sdcard
	JButton mExit;					// 退出程序
	
	JPanel pOnlineMode = new JPanel();
	JPanel pOfflineMode = new JPanel();
	JTabbedPane tp;

	private String bookPath = "";
	private String bookName = "";
	private String deviceId = "";
	private String salt = "!@#$%^&*";

	private boolean hasConnected = false;
	private boolean hasInstalled = false;

	public EncryptionToolFrame(String title) {
		super(title);

		setupUI();
		setupActions();
	}

	/**
	 * 设置界面
	 */
	private void setupUI() {
		this.setSize(new Dimension(410, 540));

		// 使用系统界面风格
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception e) {
			e.printStackTrace();
		}

		initWidgets();
		initLayout();

		this.setLocationRelativeTo(getParent()); // 居中显示
	}

	/**
	 * 初始化控件
	 */
	private void initWidgets() {
		mBookPath = new JTextField(45);
		mDeviceId = new JTextField(18);
		mDeviceId.setFont(new Font("Default", Font.PLAIN, 15));

		mInputHint = new JLabel("请输入设备ID，具体操作参考使用说明");		
		mSelectBook = new JButton("打开...");

		mDetectDevice = new JButton("检测手机");
		mDetectDevice.setPreferredSize(new Dimension(100, 45));
		mGetDeviceId = new JButton("获取设备ID");
		mGetDeviceId.setPreferredSize(new Dimension(100, 45));
		mPushToDevice = new JButton("上传");
		mPushToDevice.setPreferredSize(new Dimension(60, 45));

		mEncryptBook = new JButton("加密电子书");
		mEncryptBook.setPreferredSize(new Dimension(100, 30));
		mExit = new JButton("退出");
		mExit.setPreferredSize(new Dimension(100, 30));
		
		mDetectDevice.setToolTipText("检测当前是否有手机连接到电脑");
		mGetDeviceId.setToolTipText("每个手机唯一的ID号将用来作为加解密密钥");
		mEncryptBook.setToolTipText("开始加密所选电子书");
		mPushToDevice.setToolTipText("上传加密后的电子书到SD卡上");
		mExit.setToolTipText("退出程序");
		
		// 标签面板
		tp = new JTabbedPane();
		
		// TODO jar和工程在运行时显示大小不同
//		mLogWindow = new JTextArea(16, 46);
		mLogWindow = new JTextArea(15, 53);
		mLogWindow.setEditable(false);
	}

	/**
	 * 初始化布局
	 */
	private void initLayout() {
		this.setLayout(new BorderLayout());

		JPanel body = new JPanel();
		FlowLayout bodyLayout = new FlowLayout();
		bodyLayout.setVgap(10);
		body.setLayout(bodyLayout);

		JPanel pSelectBook = new JPanel(new FlowLayout());
		pSelectBook.setBorder(BorderFactory.createTitledBorder("选择要加密的电子书"));
		pSelectBook.add(mBookPath);
		pSelectBook.add(mSelectBook);
		
		/*
		 * 操作面板，集中了几乎所有鼠标动作
		 * 包括一个JTabPane和一个JPanel
		 * JTabPane中有两个Tab，分别为手机在线模式和离线模式
		 * JPanel中为两种模式下相同的操作
		 */
		JPanel pFunctions = new JPanel();
		FlowLayout pFunctionsLayout = new FlowLayout();
		pFunctionsLayout.setHgap(20);
		pFunctions.setLayout(pFunctionsLayout);
		
		// 手机在线模式面板
		pOnlineMode.add(mDetectDevice);
		pOnlineMode.add(mGetDeviceId);		
//		pOnlineMode.add(mPushToDevice);
		
		// 手机离线模式面板
		FlowLayout pOfflineModeLayout = new FlowLayout(FlowLayout.CENTER, 10, 7);
		pOfflineMode.setLayout(pOfflineModeLayout);
		pOfflineMode.setPreferredSize(new Dimension(250, 60));
		pOfflineMode.add(mInputHint);
		pOfflineMode.add(mDeviceId);
		
		// 增加到标签面板
		tp.addTab("Online", pOnlineMode);
		tp.addTab("Offline", pOfflineMode);
		
		// 公用按钮面板
		JPanel pCommonButtons = new JPanel();
		GridLayout pCommonButtonsLayout = new GridLayout(2, 1);
		pCommonButtonsLayout.setVgap(8);
		pCommonButtons.setLayout(pCommonButtonsLayout);
		pCommonButtons.add(mEncryptBook);
		pCommonButtons.add(mExit);
		
		pFunctions.add(tp);
		pFunctions.add(pCommonButtons);
		
		// Log输出面板
		JScrollPane sp = new JScrollPane(mLogWindow);

		body.add(pSelectBook);
		body.add(pFunctions);
		body.add(sp);

		this.add(body);
		this.setResizable(false);
	}

	/**
	 * 设置动作响应
	 */
	private void setupActions() {
		this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		this.addWindowListener(windowCloseListener);

		mSelectBook.addActionListener(selectBookListener);
		mDetectDevice.addActionListener(detectDeviceListener);
		mGetDeviceId.addActionListener(getDeviceIdListener);
		mEncryptBook.addActionListener(encryptBookListener);
		mPushToDevice.addActionListener(pushToDeviceListener);
		
		tp.addChangeListener(tabbedPanelChangeListener);
		
		mExit.addActionListener(exitListener);
	}
	
	private ActionListener pushToDeviceListener = new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			new Thread() {
				public void run() {
					String cmd = ".\\adb\\adb push ebook\\e_" + bookName +
							" /sdcard";
					System.out.println(cmd);
					writeLog(LogInfo.bookUploading);
					String res = execuateCmd(cmd);
					
					if (res == null || res.length() == 0) {
						writeLog(LogInfo.bookUploadFailed);
						return;
					}
					else
						writeLog(LogInfo.bookUploadSucc);
				}
			}.start();
		}
	};
	
	/**
	 * 监听标签页选择的变化，获得当前标签页表示的程序工作状态
	 */
	private ChangeListener tabbedPanelChangeListener = new ChangeListener() {
		public void stateChanged(ChangeEvent e) {
			if (tp.getSelectedIndex() == 0)
				mode = MODE_ONLINE;
			else if (tp.getSelectedIndex() == 1) 
				mode = MODE_OFFLINE;
		}	
	};

	/**
	 * 退出程序
	 */
	private WindowAdapter windowCloseListener = new WindowAdapter() {
		public void windowClosing(WindowEvent e) {
			if (confirmExit())
				System.exit(0);
		}
	};

	/**
	 * 选择电子书，弹出文件选择对话框
	 */
	private ActionListener selectBookListener = new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			JFileChooser fileChooser = new JFileChooser();
			fileChooser.setCurrentDirectory(new File("."));
			// TODO 增加更多格式
			FileNameExtensionFilter filter = new FileNameExtensionFilter("PDF",
					"pdf");
			fileChooser.setFileFilter(filter); // 设置文件类型过滤

			int ret = fileChooser.showOpenDialog(getParent());
			if (ret == JFileChooser.APPROVE_OPTION) { // 成功选择了文件
				bookPath = fileChooser.getSelectedFile().getAbsolutePath();
				bookName = fileChooser.getSelectedFile().getName();
				mBookPath.setText(bookPath);
				writeLog("已选择: " + bookPath);
			}

		}
	};

	/**
	 * 检测手机是否已经连接 TODO 开启新线程，设置超时退出，否则会导致界面无法响应
	 */
	private ActionListener detectDeviceListener = new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			updateWidgetsStatus(false);
			String res = execuateCmd(Command.CHECK_DEVICE);
			if (null == res) {
				writeLog(LogInfo.execuateCmdFailed);
				return;
			}
			
			switch (countString(res, "device")) {
			case 0:
				hasConnected = false;
				writeLog(LogInfo.execuateCmdFailed);
				updateWidgetsStatus(true);
				return;
			case 1:
				hasConnected = false;
				writeLog(LogInfo.noDeviceDetected);
				updateWidgetsStatus(true);
				return;
			case 2:
				hasConnected = true;
				updateWidgetsStatus(true);
				writeLog(LogInfo.oneDevcieDetected);
				break;
			case 3:
			default:
				hasConnected = false;
				writeLog(LogInfo.tooManyDevices);
				return;
			}
			
			hasInstalled = checkInstall();
			
			if (hasConnected && !hasInstalled) {
				int ret = JOptionPane.showConfirmDialog(getParent(),
						"检测到尚未安装阅读器，是否安装？",
						"安装阅读器",
						JOptionPane.YES_NO_OPTION,
						JOptionPane.QUESTION_MESSAGE);
				
				if (0 == ret) {
					writeLog(LogInfo.appInstalling);
					install();
				}
			}
			
			
		}
		
		private void install() {
			new Thread() {
				public void run() {
					updateWidgetsStatus(false);
					
					String res = execuateCmd(Command.APP_INSTALL);
					
					if (res != null && res.contains("Success")) {
						writeLog(LogInfo.installSucc);
						hasInstalled = true;
					} else {
						writeLog(LogInfo.installFailed);
					}
					
					updateWidgetsStatus(true);
				}
			}.start();
		}
		
		/**
		 * 检查电子书阅读器是否已安装，只有安装了才能自动获取设备ID
		 */
		private boolean checkInstall() {
			String res = execuateCmd(Command.CHECK_INSTALL);
			if (null == res) {
				writeLog(LogInfo.execuateCmdFailed);
				return false;
			}
			
			if (countString(res, "No such") != 0) {
				writeLog(LogInfo.appNotInstalled);
				return false;
			}
			
			writeLog(LogInfo.appInstalled);
			return true;
		}

		/**
		 * 查询toFind在src中出现的次数
		 */
		private int countString(String src, String toFind) {
			if (src.length() < toFind.length())
				return 0;
			int cnt = 0;
			int index = -1;
			while ((index = src.indexOf(toFind)) != -1) {
				cnt++;
				src = src.substring(index + toFind.length());
			}
			return cnt;
		}
	};

	/**
	 * 获取设备ID, 大多数设备ID就是手机的IMEI, 但是有例外情况
	 */
	private ActionListener getDeviceIdListener = new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			if (!hasConnected || !hasInstalled) {		
				writeLog(LogInfo.cannotGetDeviceAutomatically);
				return;
			}
			
			// 开启新线程，向手机发送广播pull出存储有设备ID的文件并读取
			new Thread() {
				public void run() {
					writeLog(LogInfo.doingGetDeviceId);
					String res1 = execuateCmd(Command.GET_DEVICE_ID);
					if (res1 == null) { // 发送广播失败
						writeLog(LogInfo.broadcastFailed);
						return;
					}
					
					File file = new File(".device_id");

					try {
						updateWidgetsStatus(false);
						sleep(2000); 	// 等待手机阅读器响应广播并执行命令
						execuateCmd(Command.PULL_FILE1);
						sleep(2000);
						if (!file.exists()) { // 从第一个路径到处文件失败
							execuateCmd(Command.PULL_FILE2); // 从另一个路径导出
							sleep(2000);
							if (!file.exists()) { // 仍然失败
								writeLog(LogInfo.getDeviceIdFailed);
								hasConnected = false;
								return;
							}
						}
						
						// 从PULL出的文件中读取设备ID
						FileInputStream fis = new FileInputStream(file);
						byte[] buf = new byte[128];	
						if (fis.read(buf) != -1) {
							deviceId = new String(buf).trim();
							writeLog(LogInfo.showDeviceID + deviceId);
						}
						fis.close();
					} catch (Exception e) {
						e.printStackTrace();
					} finally {
						updateWidgetsStatus(true);
					}
					
					// 删除临时文件
					if (file.exists())
						file.delete(); 
				}
			}.start();

		}
	};

	/**
	 * 加密电子书
	 */
	private ActionListener encryptBookListener = new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			if (bookPath.length() == 0) {
				writeLog(LogInfo.chooseBook);
				return;
			}
			
			switch (mode) {
			case MODE_ONLINE:
				if (deviceId.length() == 0) {
					writeLog(LogInfo.getDeviceId);
					return;
				}
				break;
			case MODE_OFFLINE:
				deviceId = mDeviceId.getText().trim();
				System.out.println(deviceId);
				break;
			}
			
			writeLog(LogInfo.bookEncrypting);

			(new EncryptThread()).start();
			
		}
	};
	
	private class EncryptThread extends Thread {
		public void run() {
			updateWidgetsStatus(false);
			File eBookStoreDir = new File("./ebook");
			if (!eBookStoreDir.exists())
				eBookStoreDir.mkdir();
			
			File file = new File(bookPath.trim());
			File efile = new File(eBookStoreDir + "\\e_" + bookName);
			try {
				FileInputStream fis = new FileInputStream(file);
				BufferedInputStream bis = new BufferedInputStream(fis);

				FileOutputStream fos = new FileOutputStream(efile);
				BufferedOutputStream bos = new BufferedOutputStream(fos);
				
				Encrypter worker = new Encrypter(deviceId, salt);
				worker.encrypt(bis, bos);
				
				fis.close();
				bis.close();
				fos.close();
				bos.close();
				
				writeLog(LogInfo.encryptSucc);
				
			}  catch (Exception e) {
				e.printStackTrace();
				writeLog(LogInfo.encryptError);
			} finally {
				updateWidgetsStatus(true);
			}
			
		}
	}

	/**
	 * 退出程序
	 */
	private ActionListener exitListener = new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			if (confirmExit())
				System.exit(0);
		}
	};

	/**
	 * 确认是否退出
	 * 
	 * @return true 确认退出 false 取消退出
	 */
	private boolean confirmExit() {
		int res = JOptionPane.showConfirmDialog(getParent(), "是否真的退出？", "确认退出",
				JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
		return (res == 0 ? true : false);
	}

	/**
	 * 更新控件状态
	 */
	private void updateWidgetsStatus(boolean b) {
		mDetectDevice.setEnabled(b);
		mGetDeviceId.setEnabled(b);
		mEncryptBook.setEnabled(b);
	}

	/**
	 * 执行控制台命令并返回控制台的输出信息 TODO 对包含较多命令的批处理执行效果不好
	 * 
	 * @param cmd
	 *            控制台命令
	 * @return 控制台输出
	 */
	private String execuateCmd(String cmd) {
		try {
			Process proc = Runtime.getRuntime().exec(cmd);
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					proc.getInputStream()));
			String output = "";
			String str = null;
			while ((str = reader.readLine()) != null)
				output += str;
			
			return output.trim();
		} catch (Exception exception) {
			exception.printStackTrace();
			writeLog(LogInfo.execuateCmdFailed);
		}

		return null;
	}

	/**
	 * 输出操作日志到log窗口
	 * 
	 * @param str
	 *            新的日志信息
	 */
	private void writeLog(String str) {
		String time = getTimeInString();

		String content = mLogWindow.getText();
		content += (time + "\n" + str + "\n");
		mLogWindow.setText(content);
	}

	/**
	 * 获取当前时间并以字符串形式格式化返回
	 * 
	 * @return 当前时间
	 */
	private String getTimeInString() {
		Calendar c = Calendar.getInstance();
		SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:MM:ss >> ");

		return sdf.format(c.getTimeInMillis());
	}
}
